Removing an old, cherished, yet pointless caveat "This documentation is
[supercollider.git] / Help / Streams-Patterns-Events / A Practical Guide / PG_Ref01_Pattern_Internals.html
blob2e0ea0c49a7556315ada04d0790f0633183199ff
1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5 <meta http-equiv="Content-Style-Type" content="text/css">
6 <title></title>
7 <meta name="Generator" content="Cocoa HTML Writer">
8 <meta name="CocoaVersion" content="824.48">
9 <style type="text/css">
10 p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Helvetica}
11 p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
12 p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica}
13 p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
14 p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco}
15 p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; color: #bf0000}
16 p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; min-height: 12.0px}
17 p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; color: #0000bf}
18 p.p9 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; color: #606060}
19 p.p10 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #bf0000}
20 p.p11 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; color: #bf0000}
21 p.p12 {margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica}
22 p.p13 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #0000bf}
23 span.s1 {color: #0000bf}
24 span.s2 {color: #0000bf}
25 span.s3 {color: #000000}
26 span.s4 {color: #bf0000}
27 span.s5 {color: #606060}
28 span.s6 {text-decoration: underline ; color: #0000bf}
29 span.s7 {color: #0000bf}
30 span.s8 {color: #0000bf}
31 span.s9 {font: 12.0px Helvetica}
32 span.Apple-tab-span {white-space:pre}
33 </style>
34 </head>
35 <body>
36 <p class="p1"><b>Inner workings of patterns</b></p>
37 <p class="p2"><br></p>
38 <p class="p3"><b>Patterns as streams</b></p>
39 <p class="p2"><br></p>
40 <p class="p4">As noted, patterns by themselves don't do much. They have to be turned into streams first; then, values are requested from a stream, not from the pattern.</p>
41 <p class="p2"><br></p>
42 <p class="p4">For most patterns, the stream is an instance of <a href="../../Core/Kernel/Routine.html"><span class="s1">Routine</span></a>. Routines (formally known in computer science as "coroutines") are important because they can yield control back to the caller but still remember exactly where they were, so they can resume in the middle on the next call without having to start over. A few exceptional patterns use FuncStream, which is simply a wrapper around a function that allows a function to act like a stream by responding to 'next' and other Stream methods.</p>
43 <p class="p2"><br></p>
44 <p class="p4">Every pattern class must respond to 'asStream'; however, most patterns do not directly implement asStream. Instead, they use the generic asStream implementation from <a href="../Patterns/Pattern.html"><span class="s1">Pattern</span></a>.</p>
45 <p class="p2"><br></p>
46 <p class="p5"><span class="Apple-tab-span"> </span>asStream { ^<span class="s2">Routine</span>({ <span class="s2">arg</span> inval; <span class="s2">this</span>.embedInStream(inval) }) }</p>
47 <p class="p2"><br></p>
48 <p class="p4">This line creates a Routine whose job is simply to embed the pattern into its stream. "Embedding" means for the pattern to do its assigned work, and return control to the parent level when it's finished. When a simple pattern finishes, its parent level is the Routine itself. After 'embedInStream' returns, there is nothing else for the Routine to do, so that stream is over; it can only yield nil thereafter.</p>
49 <p class="p2"><br></p>
50 <p class="p6"><span class="s3">p = </span><span class="s2">Pseries</span><span class="s3">(0, 1, 3).asStream;<span class="Apple-tab-span"> </span></span>// this will yield exactly 3 values</p>
51 <p class="p5">4.do { p.next.postln };<span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s4">// 4th value is nil</span></p>
52 <p class="p2"><br></p>
53 <p class="p4">We saw that list patterns can contain other patterns, and that the inner patterns are treated like "subroutines." List patterns do this by calling embedInStream on their list items. Most objects are embedded into the stream just by yielding the object:</p>
54 <p class="p2"><br></p>
55 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// in Object</p>
56 <p class="p5"><span class="Apple-tab-span"> </span>embedInStream { ^<span class="s2">this</span>.yield; }</p>
57 <p class="p2"><br></p>
58 <p class="p4">But if the item is a pattern itself, control enters into the subpattern and stays there until the subpattern ends. Then control goes back to the list pattern to get the next item, which is embedded and so on.</p>
59 <p class="p2"><br></p>
60 <p class="p5">p = <span class="s2">Pseq</span>([<span class="s2">Pseries</span>(0, 1, 3), <span class="s2">Pgeom</span>(10, 2, 3)], 1).asStream;</p>
61 <p class="p7"><br></p>
62 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// Pseq is embedded; first item is Pseries(0...), also embedded</p>
63 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// Control is now in the Pseries</p>
64 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// second item from Pseries</p>
65 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// third item from Pseries</p>
66 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// no more Pseries items; control goes back to Pseq</p>
67 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// Pseq gets the next item (Pgeom) and embeds it, yielding 10</p>
68 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// second item from Pgeom</p>
69 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// third item from Pgeom</p>
70 <p class="p6"><span class="s3">p.next;<span class="Apple-tab-span"> </span></span>// no more Pgeom items; Pseq has no more items, so it returns to Routine</p>
71 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// Routine has nothing left to do, so the result is nil</p>
72 <p class="p2"><br></p>
73 <p class="p4">To write a new pattern class, then, the bare minimum required is:</p>
74 <p class="p2"><br></p>
75 <ul>
76 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"><b>Instance variables</b> for the pattern's parameters</li>
77 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">A <b>*new</b> method to initialize those variables</li>
78 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">An <b>embedInStream</b> method to do the pattern's work</li>
79 </ul>
80 <p class="p2"><br></p>
81 <p class="p4">One of the simpler pattern definitions in the main library is Prand:</p>
82 <p class="p2"><br></p>
83 <p class="p8">Prand<span class="s3"> : </span>ListPattern<span class="s3"> {</span></p>
84 <p class="p5"><span class="Apple-tab-span"> </span>embedInStream { <span class="s2">arg</span> inval;</p>
85 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> item;</p>
86 <p class="p7"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></p>
87 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>repeats.value.do({ <span class="s2">arg</span> i;</p>
88 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>item = list.at(list.size.rand);</p>
89 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>inval = item.embedInStream(inval);</p>
90 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>});</p>
91 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^inval;</p>
92 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
93 <p class="p5">}</p>
94 <p class="p2"><br></p>
95 <p class="p4">This definition doesn't show the instance variables or *new method. Where are they? They are inherited from the superclass, ListPattern.</p>
96 <p class="p2"><br></p>
97 <p class="p8">ListPattern<span class="s3"> : </span>Pattern<span class="s3"> {</span></p>
98 <p class="p5"><span class="Apple-tab-span"> </span><span class="s2">var</span> &lt;&gt;list, &lt;&gt;repeats=1;</p>
99 <p class="p7"><span class="Apple-tab-span"> </span></p>
100 <p class="p5"><span class="Apple-tab-span"> </span>*new { <span class="s2">arg</span> list, repeats=1;</p>
101 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>if (list.size &gt; 0) {</p>
102 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^<span class="s2">super</span>.new.list_(list).repeats_(repeats)</p>
103 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>}{</p>
104 <p class="p9"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span><span class="s2">Error</span><span class="s3">(</span>"ListPattern ("<span class="s3"> ++ </span><span class="s2">this</span><span class="s3">.name ++ </span>") requires a non-empty collection; received "</p>
105 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>++ list ++ <span class="s5">"."</span>).throw;</p>
106 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>}</p>
107 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
108 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span></span>// some misc. methods omitted in this document</p>
109 <p class="p5">}</p>
110 <p class="p2"><br></p>
111 <p class="p4">Because of this inheritance, Prand simply expresses its behavior as a 'do' loop, choosing 'repeats' items randomly from the list and embedding them into the stream. When the loop is finished, the method returns the input value (see below).</p>
112 <p class="p2"><br></p>
113 <p class="p2"><br></p>
114 <p class="p3"><b>Streams' input values (inval, inevent)</b></p>
115 <p class="p2"><br></p>
116 <p class="p4">Before discussing input values in patterns, let's take a step back and discuss how it works for Routines.</p>
117 <p class="p2"><br></p>
118 <p class="p4"><span class="s6"><a href="../../Core/Kernel/Routine.html">Routine</a></span>'s 'next' method takes one argument, which is passed into the stream (Routine). The catch is that the routine doesn't start over from the beginning -- if it did, it would lose its unique advantage of remembering its position and resuming on demand. So it isn't sufficient to receive the argument using the routine function's argument variable.</p>
119 <p class="p2"><br></p>
120 <p class="p4">In reality, when a Routine yields a value, its execution is interrupted after calling 'yield', but before 'yield' returns. Then, when the Routine is asked for its next value, execution resumes by providing a return value from the 'yield' method. (This behavior isn't visible in the SuperCollider code in the class library; 'yield' is a primitive in the C++ backend, which is how it's able to do something that is otherwise impossible in the language.)</p>
121 <p class="p2"><br></p>
122 <p class="p4">For a quick example, consider a routine that is supposed to multiply the input value by two. First, the wrong way, assuming that everything is done by the function argument 'inval'. In reality, the first inval to come in is 1. Since nothing in the routine changes the value of inval, the routine yields the same value each time.</p>
123 <p class="p2"><br></p>
124 <p class="p8"><span class="s3">r = </span>Routine<span class="s3">({ </span>|inval|</p>
125 <p class="p5"><span class="Apple-tab-span"> </span>loop {</p>
126 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>yield(inval * 2)</p>
127 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
128 <p class="p5">});</p>
129 <p class="p7"><br></p>
130 <p class="p5">(1..3).do { <span class="s2">|x|</span> r.next(x).postln };</p>
131 <p class="p2"><br></p>
132 <p class="p4">If, instead, the routine saves the result of 'yield' into the inval variable, the routine becomes aware of the successive input values and returns the expected results.</p>
133 <p class="p2"><br></p>
134 <p class="p8"><span class="s3">r = </span>Routine<span class="s3">({ </span>|inval|</p>
135 <p class="p5"><span class="Apple-tab-span"> </span>loop {</p>
136 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// here is where the 2nd, 3rd, 4th etc. input values come in</p>
137 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>inval = yield(inval * 2);</p>
138 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
139 <p class="p5">});</p>
140 <p class="p7"><br></p>
141 <p class="p5">(1..3).do { <span class="s2">|x|</span> r.next(x).postln };</p>
142 <p class="p2"><br></p>
143 <p class="p4">This convention -- receiving the first input value as an argument, and subsequent input values as a result of a method call -- holds true for the embedInStream method in patterns also. The rules are:</p>
144 <p class="p2"><br></p>
145 <ul>
146 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">embedInStream takes <b>one argument</b>, which is the first input value.</li>
147 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">When the pattern needs to yield a value directly, or embed an item into the stream, it receives the next input value as the result of 'yield' or 'embedInStream': <b>inval = output.yield</b> or <b>output.embedInStream(inval)</b>.</li>
148 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">When the pattern exits, it must return the last input value, so that the parent pattern will get the input value as the result of its embedInStream call: <b>^inval</b>.</li>
149 </ul>
150 <p class="p2"><br></p>
151 <p class="p4">By following these rules, embedInStream becomes a near twin of yield. Both do essentially the same thing: spit values out to the user, and come back with the next input value. The only difference is that yield can return only one object to the 'next' caller, while embedInStream can yield several in succession.</p>
152 <p class="p2"><br></p>
153 <p class="p4">Take a moment to go back and look at how Prand's embedInStream method does it.</p>
154 <p class="p2"><br></p>
155 <p class="p2"><br></p>
156 <p class="p3"><b>embedInStream vs. asStream + next</b></p>
157 <p class="p2"><br></p>
158 <p class="p4">If a pattern class needs to use values from another pattern, should it evaluate that pattern using embedInStream, or should it make a separate stream (asStream) and pull values from that stream using 'next'? Both approaches are used in the class library.</p>
159 <p class="p2"><br></p>
160 <p class="p4">embedInStream turns control over to the subpattern completely. The outer pattern is effectively suspended until the subpattern gives control back. This is the intended behavior of most list patterns, for example. There is no opportunity for the parent to do anything to the value yielded back to the caller.</p>
161 <p class="p2"><br></p>
162 <p class="p4">This pattern demonstrates what it means to give control over to the subpattern. The first pattern in the Pseq list is infinite; consequently, the second subpattern will never execute because the infinite pattern never gives control back to Pseq.</p>
163 <p class="p2"><br></p>
164 <p class="p5">p = <span class="s2">Pseq</span>([<span class="s2">Pwhite</span>(0, 9, <span class="s2">inf</span>), <span class="s2">Pwhite</span>(100, 109, <span class="s2">inf</span>)], 1).asStream;</p>
165 <p class="p6"><span class="s3">p.nextN(20);<span class="Apple-tab-span"> </span></span>// no matter how long you do this, it'll never be &gt; 9!</p>
166 <p class="p2"><br></p>
167 <p class="p4">asStream should be used if the parent pattern needs to perform some other operation on the yield value before yielding, or if it needs to keep track of multiple child streams at the same time. For instance, Pdiff takes the difference between the current value and last value. Since the subtraction comes between evaluating the child pattern and yielding the difference, the child pattern must be used as a stream.</p>
168 <p class="p2"><br></p>
169 <p class="p8">Pdiff<span class="s3"> : </span>FilterPattern<span class="s3"> {</span></p>
170 <p class="p5"><span class="Apple-tab-span"> </span>embedInStream { <span class="s2">arg</span> event;</p>
171 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// here is the stream!</p>
172 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> stream = pattern.asStream;</p>
173 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> next, prev = stream.next(event);</p>
174 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>while {</p>
175 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>next = stream.next(event);</p>
176 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>next.notNil;</p>
177 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>}{</p>
178 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// and here is the return value</p>
179 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>event = (next - prev).yield;</p>
180 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>prev = next;</p>
181 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>}</p>
182 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^event</p>
183 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
184 <p class="p5">}</p>
185 <p class="p2"><br></p>
186 <p class="p2"><br></p>
187 <p class="p3"><b>Writing patterns: other factors</b></p>
188 <p class="p2"><br></p>
189 <p class="p4">Pattern objects are supposed to be <i>stateless</i>, meaning that the pattern object itself should undergo no changes based on any stream running the pattern. (There are some exceptions, such as <a href="../Patterns/Ppatmod.html"><span class="s7">Ppatmod</span></a>, which exists specifically to perform some modification on a pattern object. But, even this special case makes a separate copy of the pattern to be modified for each stream; the original pattern is insulated from the streams' behavior.) <i>Be very careful if you're thinking about breaking this rule</i>, and before doing so, think about whether there might be another way to accomplish the goal without breaking it.</p>
190 <p class="p2"><br></p>
191 <p class="p4">Because of this rule, <i>all variables reflecting the state of a particular stream should be local to the embedInStream method</i>. If you look through existing pattern classes for examples, you will see in virtually every case that embedInStream does not alter the instance variables defined in the class. It uses them as parameters, but does not change them. Anything that changes while a stream is being evaluated is a local method variable.</p>
192 <p class="p2"><br></p>
193 <p class="p4">To initialize the pattern's parameters (instance variables), typical practice in the library is to give getter and setter methods to all instance variables, and use the setters in the *new method (or, use ^super.newCopyArgs(...)). It's not typical to have an init method populate the instance variables. E.g.,</p>
194 <p class="p2"><br></p>
195 <p class="p8">Pn<span class="s3"> : </span>FilterPattern<span class="s3"> {</span></p>
196 <p class="p5"><span class="Apple-tab-span"> </span><span class="s2">var</span> &lt;&gt;repeats;</p>
197 <p class="p5"><span class="Apple-tab-span"> </span>*new { <span class="s2">arg</span> pattern, repeats=<span class="s2">inf</span>;</p>
198 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// setter method used here for repeats</p>
199 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^<span class="s2">super</span>.new(pattern).repeats_(repeats)</p>
200 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
201 <p class="p5">...</p>
202 <p class="p5">}</p>
203 <p class="p2"><br></p>
204 <p class="p4">Consider carefully whether a parameter can change in each 'next' call. If so, make a stream from that parameter and call .next(inval) on it for each iteration. Parameters that should not change, such as number of repeats, should call .value(inval) so that a function may be given. <a href="../Patterns/Pwhite.html"><span class="s7">Pwhite</span></a> demonstrates both of these features.</p>
205 <p class="p2"><br></p>
206 <p class="p4"><b>Exercise for the reader:</b> Why does Pwhite(0.0, 1.0, inf) work, even with the asStream and next calls?</p>
207 <p class="p2"><br></p>
208 <p class="p8">Pwhite<span class="s3"> : </span>Pattern<span class="s3"> {</span></p>
209 <p class="p5"><span class="Apple-tab-span"> </span><span class="s2">var</span> &lt;&gt;lo, &lt;&gt;hi, &lt;&gt;length;</p>
210 <p class="p5"><span class="Apple-tab-span"> </span>*new { <span class="s2">arg</span> lo=0.0, hi=1.0, length=<span class="s2">inf</span>;</p>
211 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^<span class="s2">super</span>.newCopyArgs(lo, hi, length)</p>
212 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
213 <p class="p5"><span class="Apple-tab-span"> </span>storeArgs { ^[lo,hi,length] }</p>
214 <p class="p5"><span class="Apple-tab-span"> </span>embedInStream { <span class="s2">arg</span> inval;</p>
215 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// lo and hi streams</p>
216 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> loStr = lo.asStream;</p>
217 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> hiStr = hi.asStream;</p>
218 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span> hiVal, loVal;</p>
219 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// length.value -- functions allowed for length</p>
220 <p class="p6"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// e.g., Pwhite could give a random number of values for each embed</p>
221 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>length.value.do({</p>
222 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>hiVal = hiStr.next(inval);</p>
223 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>loVal = loStr.next(inval);</p>
224 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>if(hiVal.isNil or: { loVal.isNil }) { ^inval };</p>
225 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>inval = rrand(loVal, hiVal).yield;</p>
226 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>});</p>
227 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>^inval;</p>
228 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
229 <p class="p5">}</p>
230 <p class="p7"><br></p>
231 <p class="p6">// the plot rises b/c the lo and hi values increase on every 'next' value</p>
232 <p class="p5"><span class="s2">Pwhite</span>(<span class="s2">Pseries</span>(0.0, 0.01, <span class="s2">inf</span>), <span class="s2">Pseries</span>(0.2, 0.01, <span class="s2">inf</span>), <span class="s2">inf</span>).asStream.nextN(200).plot;</p>
233 <p class="p2"><br></p>
234 <p class="p2"><br></p>
235 <p class="p3"><b>Cleaning up event pattern resources</b></p>
236 <p class="p2"><br></p>
237 <p class="p4">Some event patterns create server or other objects that need to be explicitly removed when they come to a stop. This is handled by the <b>EventStreamCleanup</b> object. This class stores a set of functions that will run at the pattern's end. It also uses special keys in the current event to communicate cleanup functions upward to parent patterns, and ultimately to the EventStreamPlayer that executes the events.</p>
238 <p class="p2"><br></p>
239 <p class="p4">Basic usage involves 4 stages:</p>
240 <p class="p2"><br></p>
241 <p class="p5"><span class="Apple-tab-span"> </span>embedInStream { <span class="s2">|inval|</span></p>
242 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="s2">var</span><span class="Apple-tab-span"> </span>outputEvent;</p>
243 <p class="p7"><br></p>
244 <p class="p10"><span class="s3"><b><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></b></span><b>// #1 - make the EventStreamCleanup instance</b></p>
245 <p class="p8"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>var<span class="s3"><span class="Apple-tab-span"> </span>cleanup = </span>EventStreamCleanup<span class="s3">.new;</span></p>
246 <p class="p7"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></p>
247 <p class="p10"><span class="s3"><b><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></b></span><b>// #2 - make persistent resource, and add cleanup function</b></p>
248 <p class="p11"><span class="s3"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></span>// could be some kind of resource other than a Synth</p>
249 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>synth = (... make the Synth here...);</p>
250 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>cleanup.addFunction(inval, { <span class="s2">|flag|</span></p>
251 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>if(flag) {</p>
252 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>synth.release</p>
253 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>};</p>
254 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>});</p>
255 <p class="p7"><br></p>
256 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>loop {</p>
257 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>outputEvent = (... get output event...);</p>
258 <p class="p7"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></p>
259 <p class="p10"><span class="s3"><b><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></b></span><b>// #4 - cleanup.exit</b></p>
260 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>if(outputEvent.isNil) { ^cleanup.exit(inval) };</p>
261 <p class="p7"><span class="Apple-tab-span"> </span></p>
262 <p class="p10"><span class="s3"><b><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span></b></span><b>// #3 - update the EventStreamCleanup before yield</b></p>
263 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>cleanup.update(outputEvent);</p>
264 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>inval = outputEvent.yield;</p>
265 <p class="p5"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span>}</p>
266 <p class="p5"><span class="Apple-tab-span"> </span>}</p>
267 <p class="p2"><br></p>
268 <ol>
269 <li style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">The embedInStream method should create its own instance of EventStreamCleanup. (Alternately, it may receive the cleanup object as the second argument, but it should not assume that the cleanup object will be passed in. It should always check for its existence and create the instance if needed. Note that the pattern should also reimplement asStream as shown.) It's much simpler for the pattern just to create its own instance.</li>
270 <li style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">When the pattern creates the objects that will need to be cleaned up, it should also use the <b>addFunction</b> method on the EventStreamCleanup with a function that will remove the resource(s). (The example above is deliberately oversimplified. In practice, attention to the timing of server actions is important. Several pattern classes give good examples of how to do this, e.g., Pmono, Pfx.) <br>
271 <br>
272 The flag should be used when removing Synth or Group nodes. Normally the flag is true; but, if the pattern's EventStreamPlayer gets stopped by cmd-., the nodes will already be gone from the server. If your function tries to remove them again, the user will see FAILURE messages from the server and then get confused, thinking that she did something wrong when in fact the error is preventable in the class.</li>
273 <li style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">Before calling .yield with the return event, also call <b>cleanup.update(outputEvent)</b>.</li>
274 <li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica">When embedInStream returns control back to the parent, normally this is done with ^inval. When an EventStreamCleanup is involved, it should be <b>^cleanup.exit(inval)</b>. This executes the cleanup functions and also removes them from EventStreamCleanups at any parent level.</li>
275 </ol>
276 <p class="p2"><br></p>
277 <p class="p4"><b>When does a pattern need an EventStreamCleanup?</b></p>
278 <p class="p2"><br></p>
279 <p class="p4">If the pattern creates something on the server (bus, group, synth, buffer etc.), it must use an EventStreamCleanup as shown to make sure those resources are properly garbage collected.</p>
280 <p class="p2"><br></p>
281 <p class="p4">Or, if there is a chance of the pattern stopping before one or more child patterns has stopped on its own, EventStreamCleanup is important so that the pattern is aware of cleanup actions from the children. For example, in a construction like <span class="s8">Pfindur</span>(10, <span class="s8">Pmono</span>(name, pairs...)), Pmono may continue for more than 10 beats, in which case Pfindur will cut it off. The Pmono needs to end its synth, but it doesn't know that a pattern higher up in the chain is making it stop. It becomes the parent's responsibility to clean up after the children. As illustrated above, EventStreamCleanup handles this with only minimal intrusion into normal pattern logic.</p>
282 <p class="p2"><br></p>
283 <p class="p2"><br></p>
284 <p class="p13"><span class="s3">Previous:<span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><a href="PG_Cookbook07_Rhythmic_Variations.html"><span class="s9">PG_Cookbook07_Rhythmic_Variations</span></a></span></p>
285 </body>
286 </html>